(function (fruit, util, dom, browser, $, global) {
    var document = global.document;

    function setSize(element, name, value) {
        var size = 0;
        dom.setStyle(element, name, value);
        size = (name === 'height') ? element.offsetHeight : element.offsetWidth;
        if (size > value) {
            value = value - (size - value);
            dom.setStyle(element, name, value);
        }
        return element;
    }

    var positionMap = {

        'innerCenter':function (elBound, targetBound, docScrollXY, optionOffset) {
            return {
                left:0.5 * (targetBound.width - elBound.width) + targetBound.left - docScrollXY.scrollX + optionOffset.offsetX,
                top:0.5 * (targetBound.height - elBound.height) + targetBound.top - docScrollXY.scrollY + optionOffset.offsetY
            };
        },
        'innerTop':function (elBound, targetBound, docScrollXY, optionOffset) {
            return {
                left:0.5 * (targetBound.width - elBound.width) + targetBound.left - docScrollXY.scrollX + optionOffset.offsetX,
                top:targetBound.top - docScrollXY.scrollY + optionOffset.offsetY
            };
        },
        'innerRight':function (elBound, targetBound, docScrollXY, optionOffset) {
            return {
                left:targetBound.left + targetBound.width - elBound.width - docScrollXY.scrollX + optionOffset.offsetX,
                top:0.5 * (targetBound.height - elBound.height) + targetBound.top - docScrollXY.scrollY + optionOffset.offsetY
            };
        },
        'innerBottom':function (elBound, targetBound, docScrollXY, optionOffset) {
            return {
                left:0.5 * (targetBound.width - elBound.width) + targetBound.left - docScrollXY.scrollX + optionOffset.offsetX,
                top:targetBound.height - elBound.height + targetBound.top - docScrollXY.scrollY + optionOffset.offsetY
            };
        },
        'innerLeft':function (elBound, targetBound, docScrollXY, optionOffset) {
            return {
                left:targetBound.left - docScrollXY.scrollX + optionOffset.offsetX,
                top:0.5 * (targetBound.height - elBound.height) + targetBound.top - docScrollXY.scrollY + optionOffset.offsetY
            };
        },
        'innerLeftTop':function (elBound, targetBound, docScrollXY, optionOffset) {
            return {
                left:targetBound.left - docScrollXY.scrollX + optionOffset.offsetX,
                top:targetBound.top - docScrollXY.scrollY + optionOffset.offsetY
            };
        },
        'innerRightTop':function (elBound, targetBound, docScrollXY, optionOffset) {
            return {
                left:targetBound.width - elBound.width + targetBound.left - docScrollXY.scrollX + optionOffset.offsetX,
                top:targetBound.top - docScrollXY.scrollY + optionOffset.offsetY
            };
        },
        'innerRightBottom':function (elBound, targetBound, docScrollXY, optionOffset) {
            return {
                left:targetBound.width - elBound.width + targetBound.left - docScrollXY.scrollX + optionOffset.offsetX,
                top:targetBound.height - elBound.height + targetBound.top - docScrollXY.scrollY + optionOffset.offsetY
            };
            //  });
        },
        'innerLeftBottom':function (elBound, targetBound, docScrollXY, optionOffset) {
            return {
                left:targetBound.left - docScrollXY.scrollX + optionOffset.offsetX,
                top:targetBound.height - elBound.height + targetBound.top - docScrollXY.scrollY + optionOffset.offsetY
            };
        },
        'outerTop':function (elBound, targetBound, docScrollXY, optionOffset) {
            return {
                left:0.5 * (targetBound.width - elBound.width) + targetBound.left - docScrollXY.scrollX + optionOffset.offsetX,
                top:targetBound.top - elBound.height - docScrollXY.scrollY + optionOffset.offsetY
            };
        },
        'outerRight':function (elBound, targetBound, docScrollXY, optionOffset) {
            return {
                left:targetBound.left + targetBound.width - docScrollXY.scrollX + optionOffset.offsetX,
                top:0.5 * (targetBound.height - elBound.height) + targetBound.top - docScrollXY.scrollY + optionOffset.offsetY
            };
        },
        'outerBottom':function (elBound, targetBound, docScrollXY, optionOffset) {
            return {
                left:0.5 * (targetBound.width - elBound.width) + targetBound.left - docScrollXY.scrollX + optionOffset.offsetX,
                top:targetBound.height + targetBound.top - docScrollXY.scrollY + optionOffset.offsetY
            };
        },
        'outerLeft':function (elBound, targetBound, docScrollXY, optionOffset) {
            return {
                left:targetBound.left - elBound.width - docScrollXY.scrollX + optionOffset.offsetX,
                top:0.5 * (targetBound.height - elBound.height) + targetBound.top - docScrollXY.scrollY + optionOffset.offsetY
            };
        },
        'outerLeftTop':function (elBound, targetBound, docScrollXY, optionOffset) {
            return {
                left:targetBound.left - elBound.width - docScrollXY.scrollX + optionOffset.offsetX,
                top:targetBound.top - docScrollXY.scrollY + optionOffset.offsetY
            };
        },
        'outerRightTop':function (elBound, targetBound, docScrollXY, optionOffset) {
            return {
                left:targetBound.width + targetBound.left - docScrollXY.scrollX + optionOffset.offsetX,
                top:targetBound.top - docScrollXY.scrollY + optionOffset.offsetY
            };
        },
        'outerRightBottom':function (elBound, targetBound, docScrollXY, optionOffset) {
            return {
                left:targetBound.width - elBound.width + targetBound.left - docScrollXY.scrollX + optionOffset.offsetX,
                top:targetBound.height + targetBound.top - docScrollXY.scrollY + optionOffset.offsetY
            };
        },
        'outerLeftBottom':function (elBound, targetBound, docScrollXY, optionOffset) {
            return {
                left:targetBound.left - docScrollXY.scrollX + optionOffset.offsetX,
                top:targetBound.height + targetBound.top - docScrollXY.scrollY + optionOffset.offsetY
            };
        }
    };
    fruit.define("fruit.position", {
        singleton:true,
        methods:{
            init:function () {
                util.each(this.positionMap, function (value, key) {
                    this.registerPositionMap(key, value);
                }, this);
            },
            positionMap:positionMap,
            getBound:function (inElement, inContainsMargin, isTargetFixed) {
                var offset = this.getXY(inElement), docRect;
                var targetFixed = isTargetFixed || dom.getStyle(inElement, 'position') === 'fixed';
                var bound = {
                    left:offset.left,
                    top:offset.top,
                    width:this.getOuterWidth(inElement, inContainsMargin),
                    height:this.getOuterHeight(inElement, inContainsMargin)
                };
                if (targetFixed) {
                    docRect = this.getDocRect();
                    bound.width = Math.min(docRect.width, bound.width);
                    bound.height = Math.min(docRect.height, bound.height);
                }
                return bound;
            },
            setXY:function (inElement, inCoordinates) {
                $(inElement).offset(inCoordinates);
                return inCoordinates;
            },
            getXY:function (inElement) {
                var element = inElement;
                if (inElement.nodeType !== 1) {
                    element = dom.getDocument(inElement).documentElement;
                }
                return $(element).offset();
            },
            getPosition:function (inElement) {
                return $(inElement).position();
            },
            getWidth:function (inElement) {
                return $(inElement).width();
            },
            setWidth:function (inElement, inWidth) {
                $(inElement).width(inWidth);
            },
            setHeight:function (inElement, inHeight) {
                $(inElement).height(inHeight);
            },
            getHeight:function (inElement) {
                return $(inElement).height();
            },
            getInnerWidth:function (inElement) {
                return $(inElement).innerWidth();
            },
            getInnerHeight:function (inElement) {
                return $(inElement).innerHeight();
            },
            setOuterWidth:function (inElement, inWidth) {
                return setSize(inElement, 'width', inWidth);
            },
            setOuterHeight:function (inElement, inHeight) {
                return setSize(inElement, 'height', inHeight);
            },
            getOuterWidth:function (inElement, containsMargin) {
                return $(inElement).outerWidth(containsMargin || false);
            },
            getOuterHeight:function (inElement, containsMargin) {
                return $(inElement).outerHeight(containsMargin || false);
            },
            getScroll:function (inDocument) {
                var doc = inDocument || document;
                return {
                    scrollX:Math.max((window.pageXOffset || 0), doc.documentElement.scrollLeft, doc.body.scrollLeft),
                    scrollY:Math.max((window.pageYOffset || 0), doc.documentElement.scrollTop, doc.body.scrollTop)
                };
            },
            getDocRect:function (inDocument) {
                var doc = inDocument || document;
                var window = dom.getWindow(doc),
                    root = browser.isStrict ? doc.documentElement : doc.body,
                    h = window.innerHeight || 0,
                    w = window.innerWidth || 0,
                    scrollW = root.scrollWidth,
                    scrollH = root.scrollHeight,
                    scrollXY = this.getScroll(doc);
                if (browser.compatMode && !browser.opera) {
                    w = root.clientWidth;
                    h = root.clientHeight;
                }
                scrollW = Math.max(scrollW, w);
                scrollH = Math.max(scrollH, h);
                return {
                    width:w,
                    height:h,
                    scrollWidth:scrollW,
                    scrollHeight:scrollH,
                    scrollX:scrollXY.scrollX,
                    scrollY:scrollXY.scrollY
                };
            },
            setPosition:function (inElement, inOptions) {
                var positionAction = positionMap[inOptions.position || "innerCenter"];
                var bound = positionAction ? positionAction(inElement, inOptions) : inOptions.position;
                return dom.setStyles(inElement, bound);
            },
            __getTargetBound:function (target, isFixed) {
                // for spicific point
                if (target.x) {
                    return {
                        left:target.x,
                        top:target.y,
                        width:0,
                        height:0
                    }
                } else {
                    return this.getBound(target, null, isFixed)
                }
            },
            getOffset:function(inElement){
                return $(inElement).offset();
            },
            __getOffset:function (inElement, inTarget, inProcess, inOptions) {
                var elBound = this.getBound(inElement);
                // detect element or target is fixed
                //TODO:inTarget:document.
                var isFixed = inOptions.fixed || ( inTarget._element && (dom.getStyle(inTarget, 'position') === 'fixed' || dom.getStyle(inElement, 'position') === 'fixed'));
                var targetBound = this.__getTargetBound(inTarget, isFixed);
                //get page scrollx&y
                var docScrollXY = (!isFixed || util.isWindow(inTarget) || inTarget === document) ? {
                    scrollX:0,
                    scrollY:0
                } : this.getScroll();
                var optionOffset = {
                    offsetX:inOptions.offsetX,
                    offsetY:inOptions.offsetY
                };
                var styleObj = inProcess.call(this, elBound, targetBound, docScrollXY, optionOffset);
                return util.extend(styleObj, {
                    position:isFixed ? 'fixed' : 'absolute'
                });
            },
            registerPositionMap:function (inPositionKey, inAction) {
                var self = this;
                positionMap[inPositionKey] = function (inElement, inOptions) {
                    return self.__getOffset(inElement, inOptions.target, inAction, inOptions);
                };
            }
        }
    })
})(fruit, fruit.util, fruit.dom, fruit.browser, jQuery, window);
